package crp.core.course.domain.repositories;

import com.qwlabs.lang.C2;
import com.qwlabs.lang.F2;
import com.qwlabs.lang.S2;
import com.qwlabs.panache.Where;
import crp.core.course.domain.models.CourseEntity;
import crp.core.course.domain.models.CourseSourceType;
import crp.core.course.domain.models.ExistedCourseSource;
import crp.core.shared.PublicStatus;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;

import javax.enterprise.context.ApplicationScoped;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import static io.quarkus.panache.common.Parameters.with;

@ApplicationScoped
public class CourseRepository implements PanacheRepositoryBase<CourseEntity, String> {

    public Stream<CourseEntity> findByIds(Set<String> ids, PublicStatus... publicStatuses) {
        if (C2.isEmpty(ids)) {
            return Stream.empty();
        }
        Where where = Where.create().and("id in (:ids)", "ids", ids);
        if (C2.isNotEmpty(publicStatuses)) {
            where.and("attributes.publicStatus in (:publicStatuses)", "publicStatuses", C2.list(publicStatuses));
        }
        return find(where.get(), where.getParameters()).stream();
    }

    public Stream<CourseEntity> findByLatest(Integer limit, PublicStatus... publicStatuses) {
        if (C2.isEmpty(publicStatuses)) {
            return Stream.empty();
        }
        return find("attributes.publicStatus in (:publicStatuses)", Sort.descending("createdAt"), Parameters.with("publicStatuses", C2.list(publicStatuses))).page(0, limit).firstPage().stream();
    }

    public PanacheQuery<CourseEntity> findBy(String keyword, String academicTermId, String openDepartmentId, CourseSourceType sourceType,
                                             ExistedCourseSource existedCourseSource) {
        Where where = Where.create();
        S2.ifNotBlank(keyword, () -> {
            where.and(Where.create().and("code like :keyword", "keyword", "%" + keyword + "%").or("name like :keyword", "keyword", "%" + keyword + "%"));
        });
        S2.ifNotBlank(academicTermId, () -> {
            where.and("attributes.academicTerm.id=:academicTermId", "academicTermId", academicTermId);
        });
        S2.ifNotBlank(openDepartmentId, () -> {
            where.and("attributes.openDepartment.id=:openDepartmentId", "openDepartmentId", openDepartmentId);
        });
        F2.ifPresent(sourceType, () -> {
            where.and("attributes.sourceType=:sourceType", "sourceType", sourceType);
        });

        if (existedCourseSource == ExistedCourseSource.YES) {
            where.and("id in (select distinct course.id from CourseResourceEntity )");
        } else if (existedCourseSource == ExistedCourseSource.NO) {
            where.and("id not in (select distinct course.id from CourseResourceEntity )");
        }

        return find(where.get(), where.getParameters());
    }

    public PanacheQuery<CourseEntity> findBy(String keyword, PublicStatus publicStatus) {
        Where where = Where.create().and("attributes.publicStatus=:publicStatus", "publicStatus", publicStatus);
        S2.ifNotBlank(keyword, () -> {
            where.and(Where.create().and("code like :keyword", "keyword", "%" + keyword + "%").or("name like :keyword", "keyword", "%" + keyword + "%"));
        });
        return find(where.get(), where.getParameters());
    }

    public Optional<CourseEntity> findByCode(String code) {
        return find("code=:code", with("code", code)).firstResultOptional();
    }


    public PanacheQuery<CourseEntity> findByIds(Set<String> ids, String academicTermId) {
        Where where = Where.create().and("id in (:ids)", "ids", ids);
        S2.ifNotBlank(academicTermId, () -> {
            where.and("attributes.academicTerm.id=:academicTermId", "academicTermId", academicTermId);
        });
        return find(where.get(), where.getParameters());
    }

    public PanacheQuery<CourseEntity> findByFacultyId(String facultyId, String academicTermId) {
        Where where = Where.create().and("id in (select course.id from CourseFacultyEntity where faculty.id=:facultyId)", "facultyId", facultyId);
        S2.ifNotBlank(academicTermId, () -> {
            where.and("attributes.academicTerm.id=:academicTermId", "academicTermId", academicTermId);
        });
        return find(where.get(), where.getParameters());
    }

    public PanacheQuery<CourseEntity> findByIdsAndKeyword(Set<String> ids, String keyword, PublicStatus publicStatus) {
        Where where = Where.create();
        S2.ifNotBlank(keyword, () -> {
            where.and(Where.create().and("code like :keyword", "keyword", "%" + keyword + "%")
                    .or("name like :keyword", "keyword", "%" + keyword + "%"));
        });
        where.and(Where.create().and("attributes.publicStatus=:publicStatus", "publicStatus", publicStatus).or("id in (:ids)", "ids", ids));
        return find(where.get(), where.getParameters());
    }

    public PanacheQuery<CourseEntity> findByFacultyIdAndKeyword(String facultyId, String keyword, PublicStatus publicStatus) {
        Where where = Where.create();
        S2.ifNotBlank(keyword, () -> {
            where.and(Where.create().and("code like :keyword", "keyword", "%" + keyword + "%").or("name like :keyword", "keyword", "%" + keyword + "%"));
        });
        where.and(Where.create().and("attributes.publicStatus=:publicStatus", "publicStatus", publicStatus)
                .or("id in (select course.id from CourseFacultyEntity where faculty.id=:facultyId)", "facultyId", facultyId));
        return find(where.get(), where.getParameters());
    }
}
